package org.jing.ext.socket;

import org.jing.core.lang.JingException;
import org.jing.core.logger.JingLogger;
import org.jing.core.util.ClassUtil;

import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

/**
 * Description: <br>
 *
 * @author bks <br>
 * @since 2024-07-08 <br>
 */
public final class SocketServerDispatcher extends Thread {
    private static final JingLogger LOGGER = JingLogger.getLogger(SocketServerDispatcher.class);

    private BaseSocketServerConfig config;

    private Class<? extends BaseSocketServerOperator> operatorClass;

    private SocketServerDispatcher() {}

    public static SocketServerDispatcher createDispatcher(BaseSocketServerConfig config, Class<? extends BaseSocketServerOperator> operatorClass) {
        SocketServerDispatcher dispatcher = new SocketServerDispatcher();
        dispatcher.config = config;
        dispatcher.operatorClass = operatorClass;
        return dispatcher;
    }

    @Override public void run() {
        String code = config.getCode();
        int port = config.getPort();
        String encoding = config.getEncoding();
        long timeout = config.getTimeout();
        int limit = config.getLimit();
        limit = -1 == limit ? Integer.MAX_VALUE : limit;
        Thread.currentThread().setName("socket-server-dispatcher-" + code);
        LOGGER.info(">>>Socket Server Dispatcher [{}]<<<", code);
        LOGGER.info("[{}].{}: {}", code, Const.KEY_NAME, config.getName());
        LOGGER.info("[{}].{}: {}", code, Const.KEY_PORT, port);
        LOGGER.info("[{}].{}: {}", code, Const.KEY_ENCODING, encoding);
        LOGGER.info("[{}].{}: {}", code, Const.KEY_TIMEOUT, timeout);
        LOGGER.info("[{}].{}: {}", code, Const.KEY_LIMIT, limit);
        BaseSocketServerOperator operator;
        try (ServerSocket server = new ServerSocket(port)) {
            ExecutorService executor = Executors.newCachedThreadPool(SocketThreadFactory.createFactory("socket-operator-" + config.getCode()));
            Semaphore semaphore = new Semaphore(limit);
            boolean flag = true;
            while (flag) {
                Socket client = server.accept();
                operator = ClassUtil.createInstance(operatorClass);
                operator.init(config, client, semaphore);
                executor.execute(operator);
            }
            executor.shutdown();
        }
        catch (JingException e) {
            throw e;
        }
        catch (Throwable t) {
            LOGGER.error(t);
            Thread.currentThread().interrupt();
        }
    }
}
